home *** CD-ROM | disk | FTP | other *** search
/ NeXT Education Software Sampler 1992 Fall / NeXT Education Software Sampler 1992 Fall.iso / Programming / Source / loTeX / loTeX.app / Subprocess.m < prev    next >
Encoding:
Text File  |  1992-07-24  |  7.6 KB  |  331 lines

  1. /* Modified 21 July 92 by Derek Beatty.  The code needed a wait call. */
  2. /*
  3.     Subprocess.m    (v10)
  4.     by Charles L. Oei
  5.     pty support by Joe Freeman
  6.     Subprocess Example, Release 2.0
  7.     NeXT Computer, Inc. 
  8.  
  9.     You may freely copy, distribute and reuse the code in this example.
  10.     NeXT disclaims any warranty of any kind, expressed or implied, as to
  11.     its fitness for any particular use.
  12. */
  13.  
  14. #import "Subprocess.h"
  15. // #import <sgtty.h>    // needed to compile under Release 1.0
  16. #import <appkit/nextstd.h>
  17. #import <appkit/Application.h>
  18. #import <appkit/Panel.h>
  19.  
  20. #import <sys/time.h>        // beatty
  21. #import <sys/resource.h>    // beatty
  22.  
  23. #define    PTY_TEMPLATE "/dev/pty??"
  24. #define    PTY_LENGTH 11
  25.  
  26. static void showError();
  27.  
  28. static union wait wait_status;        // beatty
  29. static struct rusage wait_rusage;    // beatty
  30.  
  31.  
  32. /*==========================================================
  33.  *
  34.  * Private Instance Methods
  35.  *
  36.  *==========================================================*/
  37.  
  38. @interface Subprocess(Private)
  39. - childDidExit;
  40. - fdHandler:(int)theFd;
  41. @end
  42.  
  43. @implementation Subprocess(Private)
  44.  
  45. - childDidExit
  46.     // cleanup after a child process exits
  47. {
  48.     if (childPid)
  49.     {
  50.     DPSRemoveFD(fromChild);
  51.     close(fromChild);
  52.     fclose(fpToChild);
  53.     childPid=0;    // specify that child is dead
  54.     if (delegate && [delegate respondsTo:@selector(subprocessDone)])
  55.         [delegate perform:@selector(subprocessDone)];
  56.     }
  57.     return self;
  58. }
  59.  
  60. - fdHandler:(int)theFd
  61.     // DPS handler for output from subprocess
  62. {
  63.     if (((bufferCount = read(theFd, outputBuffer, BUFFERSIZE-1)) < 0) ||
  64.       (!bufferCount))
  65.     {
  66.     // beatty: avoid zombies
  67.      while (wait3(&wait_status, WNOHANG, &wait_rusage) > 0)
  68.          continue;
  69.     [self childDidExit];
  70.     return self;
  71.     }
  72.     outputBuffer[bufferCount] = '\0';
  73.     if (delegate && [delegate respondsTo:@selector(subprocessOutput:)])
  74.     [delegate perform:@selector(subprocessOutput:)
  75.           with:(void *)&outputBuffer];
  76.     return self;
  77. }
  78.  
  79. @end
  80.  
  81.  
  82. /*==========================================================
  83.  *
  84.  * Private Utility Routines
  85.  *
  86.  *==========================================================*/
  87.  
  88. static void
  89. showError (const char *errorString, id theDelegate)
  90.     // ensure errors never get dropped on the floor
  91. {
  92.     if (theDelegate && [theDelegate respondsTo:@selector(subprocessError:)])
  93.     [theDelegate
  94.         perform:@selector(subprocessError:)
  95.         with:(void *)errorString];
  96.     else if (NXApp)    // no delegate, but we're running w/in an App
  97.     NXRunAlertPanel(0, errorString, 0, 0, 0);
  98.     else
  99.     perror(errorString);
  100. }
  101.  
  102. static void
  103. fdHandler (int theFd, id self)
  104.     // DPS handler for output from subprocess
  105. {
  106.     [self fdHandler:theFd];
  107. }
  108.  
  109. static void
  110. getptys (int *master, int *slave)
  111.     // attempt to setup the ptys
  112. {
  113.     char device[PTY_LENGTH];
  114.     char *block, *num;
  115.     char *blockLoc; // specifies the location of block for the device string
  116.     char *numLoc; // specifies the pty name with the digit ptyxD
  117.     char *msLoc; // specifies the master (ptyxx) or slave (ttyxx)
  118.     
  119.     struct sgttyb setp =
  120.     {B9600, B9600, (char)0x7f, (char)0x15, (CRMOD|ANYP)};
  121.     struct tchars setc =
  122.     {CINTR, CQUIT, CSTART, CSTOP, CEOF, CBRK};
  123.     struct ltchars sltc =
  124.     {CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT};
  125.     int    lset =
  126.     (LCRTBS|LCRTERA|LCRTKIL|LCTLECH|LPENDIN|LDECCTQ);
  127.     int    setd = NTTYDISC;
  128.     
  129.     strcpy(device, PTY_TEMPLATE); // string constants are not writable
  130.     blockLoc = &device[ strlen("/dev/pty") ];
  131.     numLoc = &device[ strlen("/dev/pty?") ];
  132.     msLoc = &device[ strlen("/dev/") ];
  133.     for (block = "pqrs"; *block; block++)
  134.     {
  135.     *blockLoc = *block;
  136.     for (num = "0123456789abcdef"; *num; num++)
  137.     {
  138.         *numLoc = *num;
  139.         *master = open(device, O_RDWR);
  140.         if (*master >= 0)
  141.         {
  142.         *msLoc = 't';
  143.         *slave = open(device, O_RDWR);
  144.         if (*slave >= 0)
  145.         {
  146.             (void) ioctl(*slave, TIOCSETP, (char *)&setp);
  147.             (void) ioctl(*slave, TIOCSETC, (char *)&setc);
  148.             (void) ioctl(*slave, TIOCSETD, (char *)&setd);
  149.             (void) ioctl(*slave, TIOCSLTC, (char *)&sltc);
  150.             (void) ioctl(*slave, TIOCLSET, (char *)&lset);
  151.             return;
  152.         }
  153.         }
  154.     } /* hunting through a bank of ptys */
  155.     } /* hunting through blocks of ptys in all the right places */
  156.     *master = -1;
  157.     *slave = -1;
  158. }
  159.  
  160.  
  161. @implementation Subprocess
  162.  
  163. /*==========================================================
  164.  *
  165.  * Public Instance Methods
  166.  *
  167.  *==========================================================*/
  168.  
  169. - init:(const char *)subprocessString
  170.     // a cover for the below withDelegate:nil, andPtySupport:NO, andStdErr:YES
  171. {
  172.     return
  173.     [self
  174.         init:subprocessString
  175.         withDelegate:nil
  176.         andPtySupport:NO
  177.         andStdErr:YES];
  178. }
  179.  
  180. - init:(const char *)subprocessString
  181.     withDelegate:theDelegate
  182.     andPtySupport:(BOOL)wantsPty
  183.     andStdErr:(BOOL)wantsStdErr
  184.     // initializes an instance of Subprocess and corresponding UNIX process
  185. {
  186.     int pipeTo[2];        // for non-Pty support
  187.     int pipeFrom[2];
  188.     int    tty, numFds, fd;    // for temporary use
  189.     int processGroup;
  190.     int pidChild;        // needed because childPid does not exist
  191.                 // until Subprocess is instantiated
  192.  
  193.     if (wantsPty)
  194.     {
  195.         tty = open("/dev/tty", O_RDWR);
  196.     getptys(&masterPty,&slavePty);
  197.     if (masterPty <= 0 || slavePty <= 0)
  198.     {
  199.         showError("Error grabbing ptys for subprocess.", theDelegate);
  200.         return self;
  201.     }
  202.     // remove the controlling tty if launched from a shell,
  203.     // but not Workspace;
  204.     // so that we have job control over the parent application in shell
  205.     // and so that subprocesses can be restarted in Workspace
  206.     if  ((tty<0) && ((tty = open("/dev/tty", 2))>=0))
  207.     {
  208.         ioctl(tty, TIOCNOTTY, 0);
  209.         close(tty);
  210.     }
  211.     }
  212.     else
  213.     {
  214.     if (pipe(pipeTo) < 0 || pipe(pipeFrom) < 0)
  215.     {
  216.         showError("Error starting UNIX pipes to subprocess.", theDelegate);
  217.         return self;
  218.     }
  219.     }
  220.     
  221.     switch (pidChild = vfork())
  222.     {
  223.     case -1:    // error
  224.     showError("Error starting UNIX vfork of subprocess.", theDelegate);
  225.     return self;
  226.  
  227.     case 0:    // child
  228.     if (wantsPty)
  229.     {
  230.         dup2(slavePty, 0);
  231.         dup2(slavePty, 1);
  232.         if (wantsStdErr)
  233.         dup2(slavePty, 2);
  234.     }
  235.     else
  236.     {
  237.         dup2(pipeTo[0], 0);
  238.         dup2(pipeFrom[1], 1);
  239.         if (wantsStdErr)
  240.         dup2(pipeFrom[1], 2);
  241.     }
  242.     
  243.     numFds = getdtablesize();
  244.     for (fd=3; fd<numFds; fd++)
  245.         close(fd);
  246.  
  247.     processGroup = getpid();
  248.     if (wantsPty)            // beatty (?will this work?)
  249.         ioctl(0, TIOCSPGRP, (char *)&processGroup);
  250.     setpgrp (0, processGroup);
  251.     
  252.     // we exec a /bin/sh so that cmds are easier to specify for the user
  253.     execl("/bin/sh", "sh", "-c", subprocessString, 0);
  254.     perror("vfork (child)"); // should never gets here tho
  255.     exit(1);
  256.  
  257.     default:    // parent
  258.     [self setDelegate:theDelegate];
  259.     childPid = pidChild;
  260.  
  261.     if (wantsPty)
  262.     {
  263.         close(slavePty);
  264.         
  265.         fpToChild = fdopen(masterPty, "w");
  266.         fromChild = masterPty;
  267.     }
  268.     else
  269.     {
  270.         close(pipeTo[0]);
  271.         close(pipeFrom[1]);
  272.     
  273.         fpToChild = fdopen(pipeTo[1], "w");
  274.         fromChild = pipeFrom[0];
  275.     }
  276.  
  277.     setbuf(fpToChild, NULL);
  278.     DPSAddFD(
  279.         fromChild,
  280.         (DPSFDProc)fdHandler,
  281.         (id)self,
  282.         NX_MODALRESPTHRESHOLD+1);
  283.     return self;
  284.     }
  285. }
  286.  
  287. - send:(const char *)string withNewline:(BOOL)wantNewline
  288. {
  289.     fputs(string, fpToChild);
  290.     if (wantNewline)
  291.         fputc('\n', fpToChild);
  292.     return self;
  293. }
  294.  
  295. - send:(const char *)string
  296. {
  297.     [self send:string withNewline:YES];
  298.     return self;
  299. }
  300.  
  301. - terminateInput
  302.     // effectively sends an EOF to the child process stdin
  303. {
  304.     fclose(fpToChild);
  305.     return self;
  306. }
  307.  
  308. - terminate:sender
  309. {
  310.     if (childPid)
  311.     {
  312.     // kill(childPid+1, SIGTERM);    // beatty: doesn't +1 assume too much?
  313.     killpg(childPid, SIGTERM);    // beatty
  314.     [self childDidExit];
  315.     }
  316.     return self;
  317. }
  318.  
  319. - setDelegate:anObject
  320. {
  321.     delegate = anObject;
  322.     return self;
  323. }
  324.  
  325. - delegate
  326. {
  327.     return delegate;
  328. }
  329.  
  330. @end
  331.